package br.unicamp.ic.lis.dbvapi.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.genericspatialdao.dao.DAO;
import org.genericspatialdao.dao.GenericSpatialDAO;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

import br.unicamp.ic.lis.dbvapi.domain.DBV;
import br.unicamp.ic.lis.dbvapi.domain.Scale;
import br.unicamp.ic.lis.dbvapi.service.DBVService;
import br.unicamp.ic.lis.dbvapi.utils.StampUtils;

public class DBVServiceImpl extends ServiceImpl<DBV> implements DBVService {

	private static Logger LOG = Logger.getLogger(DBVServiceImpl.class);

	public DBVServiceImpl() {
		super(DBV.class);
	}

	@Override
	public DAO<DBV> getDAO() {
		if (dao == null) {
			dao = new GenericSpatialDAO<DBV>(DBV.class) {
				@Override
				public void persist(DBV dbv) {
					if (StringUtils.isBlank(dbv.getStamp())) {
						setStamp(dbv);
					}
					updateNextChildAfterPersist(dbv, dbv.getParent());
					super.persist(dbv);
				}

				/*
				 * public void remove(List<DBV> list) { // root must be the last
				 * to be removed Collections.reverse(list); super.remove(list);
				 * // update parent nextChild for all DBVs removed List<DBV>
				 * parentList = new ArrayList<DBV>(); for (DBV dbv : list) { DBV
				 * parent = dbv.getParent(); if (parent != null) {
				 * updateNextChildAfterRemove(parent); parentList.add(parent); }
				 * } merge(parentList); }
				 */
				@Override
				public void remove(DBV dbv) {
					super.remove(dbv);
					if (dbv.getParent() != null) {
						// verifies if the parent has already removed
						DBV parent = find(dbv.getParent().getId());
						if (parent != null) {
							updateNextChildAfterRemove(parent);
							merge(parent);
						}
					}
				}
			};
		}
		return dao;
	}

	@Override
	public List<DBV> getDBVs(String stamp) {
		List<Criterion> list = new ArrayList<Criterion>();
		list.add(Restrictions.eq("stamp", stamp));
		return getDAO().findByCriteria(list);
	}

	@Override
	public DBV getDBV(String stamp, long... sid) {
		List<Criterion> list = new ArrayList<Criterion>();
		list.add(Restrictions.eq("stamp", stamp));
		list.add(Restrictions.eq("sid", sid[0]));
		return getDAO().findUniqueByCriteria(list);
	}

	@Override
	public DBV getParent(DBV dbv) {
		return dbv.getParent();
	}

	@Override
	public DBV getRoot(Scale scale) {
		return getDBV(StampUtils.ROOT_STAMP, scale.getId());
	}

	@Override
	public List<DBV> getDBVs(Scale scale) {
		List<Criterion> list = new ArrayList<Criterion>();
		list.add(Restrictions.eq("scale", scale));
		return getDAO().findByCriteria(list);
	}

	/*
	 * @Override public void setIRI(DBV dbv) { StringBuilder iri = new
	 * StringBuilder(IRIUtils.getBaseIRI(dbv)); iri.append(dbv.getStamp());
	 * iri.append(ConstantsUtils.SLASH); iri.append(dbv.getScale()); String
	 * finalIRI = iri.toString(); dbv.setIRI(finalIRI); }
	 */

	private void setStamp(DBV dbv) {
		DBV parent = getParent(dbv);
		if (parent == null) {
			dbv.setStamp(StampUtils.ROOT_STAMP);
			dbv.setNextChild(StampUtils.FIRST_CHILD);
			return;
		}
		dbv.setStamp(parent.getNextChild());
	}

	private void updateNextChildAfterPersist(DBV dbv, DBV parent) {
		LOG.info("Updating next child after persist for DBV: " + dbv
				+ " and for parent: " + parent);
		final String DOT_ONE = ".1";

		// dbv is root
		if (parent == null) {
			dbv.setNextChild(StampUtils.FIRST_CHILD);
			return;
		}

		// update nextChild of parent - increment last number
		List<String> chainStamp = StampUtils.getStampChainAsList(parent
				.getNextChild());
		int lastIndex = chainStamp.size() - 1;
		Integer aux = Integer.valueOf(chainStamp.get(lastIndex));
		chainStamp.set(lastIndex, String.valueOf(++aux));
		String parentNextChild = StampUtils.buildStamp(chainStamp);
		LOG.debug("New parent nextChild: " + parentNextChild);
		parent.setNextChild(parentNextChild);

		// update nextChild of actual dbv
		String nextChild = dbv.getStamp() + DOT_ONE;
		LOG.debug("New dbv nextChild: " + nextChild);
		dbv.setNextChild(nextChild);
	}

	private void updateNextChildAfterRemove(DBV parent) {
		LOG.info("Updating next child for parent: " + parent
				+ " after remove a child");
		/*
		 * nextChild has already the first child if
		 * (parent.getNextChild().equalsIgnoreCase(StampUtils.FIRST_CHILD)) {
		 * return; }
		 */
		List<String> chainStamp = StampUtils.getStampChainAsList(parent
				.getNextChild());
		int lastIndex = chainStamp.size() - 1;
		Integer aux = Integer.valueOf(chainStamp.get(lastIndex));
		chainStamp.set(lastIndex, String.valueOf(--aux));
		String parentNextChild = StampUtils.buildStamp(chainStamp);
		LOG.debug("New parent nextChild: " + parentNextChild);
		parent.setNextChild(parentNextChild);
	}

	/*
	 * @Override public void setStamp(DBV dbv) { DBV parent = getParent(dbv); if
	 * (parent == null) { dbv.setStamp(StampUtils.ROOT_STAMP);
	 * dbv.setNextChild(StampUtils.FIRST_CHILD); return; }
	 * dbv.setStamp(parent.getNextChild()); }
	 */

	/*
	 * private void updateNextChild(DBV dbv, DBV parent) { final String DOT_ONE
	 * = ".1";
	 * 
	 * // dbv is root if (parent == null) {
	 * dbv.setNextChild(StampUtils.FIRST_CHILD); return; }
	 * 
	 * // update nextChild of parent String parentNextChild =
	 * parent.getNextChild() + DOT_ONE; parent.setNextChild(parentNextChild);
	 * 
	 * // update nextChild of actual dbv List<String> chainStamp = StampUtils
	 * .getStampChainAsList(dbv.getStamp()); int lastIndex = chainStamp.size() -
	 * 1; Integer aux = Integer.valueOf(chainStamp.get(lastIndex));
	 * chainStamp.set(lastIndex, String.valueOf(++aux)); String nextChild =
	 * StampUtils.buildStamp(chainStamp); dbv.setNextChild(nextChild); }
	 */
}
